package analizador.lexico;

import java.io.InputStream;
import java.io.IOException;

import categoria.lexica.CatLexica;

import categoria.token.Token;

enum EstadoLexico {

	INICIAL, ES_INICIO_ASIGNACION, ES_ASIGNACION, ES_COMENTARIO, ES_NUMERO, ES_IDENTIFICADOR, ES_MAS, ES_MENOS, ES_POR, ES_ENTRE, 
	ES_INICIO_FLECHA, ES_FLECHA, ES_PARENT_INICIAL, ES_PARENT_FINAL, ES_CORCHETE_INICIAL, ES_CORCHETE_FINAL, ES_EOF,ES_AMPERSAND,
	ES_PUNTOYCOMA
};

public class AnalizadorLexicoLengDescAlgoritmica {

	private final static int EOF = -1;
	private int caract;
	private EstadoLexico estado;
	private InputStream input;
	private String lexema;
	private int fila;
	private int col;
	private int filaInicio;
	private int colInicio;

	public AnalizadorLexicoLengDescAlgoritmica(InputStream input)
			throws IOException {
		this.input = input;
		fila = 0;
		col = -1;
		caract = sigCar();
	}

	public Token sigToken() throws IOException {
		estado = EstadoLexico.INICIAL;
		filaInicio = fila;
		colInicio = col;
		lexema = "";
		while (true) {
			System.out.println("Carac: "+(char) caract+" Estado: "+estado.toString()+" Lexema: "+lexema);
			switch (estado) {
			case INICIAL:
				if (esLetra(caract) || caract == '_') {
					transita(EstadoLexico.ES_IDENTIFICADOR);
				} else if (esDigito(caract)) {
					transita(EstadoLexico.ES_NUMERO);
				} else if (caract == ':') {
					transita(EstadoLexico.ES_INICIO_ASIGNACION);
				} else if (caract == '[') {
					transita(EstadoLexico.ES_CORCHETE_INICIAL);
				} else if (caract == ']') {
					transita(EstadoLexico.ES_CORCHETE_FINAL);
				} else if (caract == '(') {
					transita(EstadoLexico.ES_PARENT_INICIAL);
				} else if (caract == ')') {
					transita(EstadoLexico.ES_PARENT_FINAL);
				} else if (caract == '+') {
					transita(EstadoLexico.ES_MAS);
				} else if (caract == '-') {
					transita(EstadoLexico.ES_MENOS);
				} else if (caract == '*') {
					transita(EstadoLexico.ES_POR);
				} else if (caract == '/') {
					transita(EstadoLexico.ES_ENTRE);
				} else if (caract == EOF) {
					transita(EstadoLexico.ES_EOF);
				} else if (caract == '@') {
					transitaIgnorando(EstadoLexico.ES_COMENTARIO);
				}else if (caract == '&') {
					transita(EstadoLexico.ES_AMPERSAND);
				}else if (caract == ';') {
						transita(EstadoLexico.ES_PUNTOYCOMA);
				}else if (esSep(caract)) {
					transitaIgnorando(EstadoLexico.INICIAL);
				} else {
					errorLexico();
				}
				break;
				
			case ES_INICIO_ASIGNACION:
				if (caract == '=') {
					transita(EstadoLexico.ES_ASIGNACION); break;
				}
			case ES_INICIO_FLECHA:
				if (caract == '>') {
					transita(EstadoLexico.ES_FLECHA);break;
				}
			case ES_CORCHETE_INICIAL:
				return new Token(filaInicio, colInicio,
						CatLexica.CORCHETEAPERTURA);
			case ES_CORCHETE_FINAL:
				return new Token(filaInicio, colInicio,
						CatLexica.CORCHETECIERRE);
			case ES_PARENT_INICIAL:
				return new Token(filaInicio, colInicio,
						CatLexica.PARENTESISAPERTURA);
			case ES_PARENT_FINAL:
				return new Token(filaInicio, colInicio,
						CatLexica.PARENTESISCIERRE);
			case ES_MAS:
				return new Token(filaInicio, colInicio, CatLexica.MAS);
			case ES_MENOS:
				if (caract == '>') {
            		transita(EstadoLexico.ES_FLECHA);
            		break;
            	}
            	else {
            		return new Token(filaInicio, colInicio, CatLexica.MENOS);
            	}	
			case ES_POR:
				return new Token(filaInicio, colInicio, CatLexica.POR);
			case ES_ENTRE:
				return new Token(filaInicio, colInicio, CatLexica.ENTRE);
			case ES_AMPERSAND:
				return new Token(filaInicio, colInicio, CatLexica.AMPERSAND);
			case ES_PUNTOYCOMA:
				return new Token(filaInicio, colInicio, CatLexica.PUNTOCOMA);
			case ES_ASIGNACION:
				return new Token(filaInicio, colInicio, CatLexica.ASIGNACION);
			case ES_FLECHA:
				return new Token(filaInicio, colInicio, CatLexica.FLECHA);
			case ES_NUMERO:
				if (esDigito(caract)) {
					transita(EstadoLexico.ES_NUMERO);
				} else {
					return new Token(filaInicio, colInicio, CatLexica.NUMERO,
							lexema);
				}
				break;
			case ES_IDENTIFICADOR:
				if (esLetra(caract) || esDigito(caract) || caract == '_') {
					transita(EstadoLexico.ES_IDENTIFICADOR);
				} else {
					return tokenSimbolo();
				}
				break;
			case ES_EOF:
				return new Token(filaInicio, colInicio, CatLexica.EOF);
			case ES_COMENTARIO:
            	if (caract == '\n') {
            		transitaIgnorando(EstadoLexico.INICIAL);
            		lexema = "";
            	}
            	else if (caract == EOF) {
            		transita(EstadoLexico.ES_EOF);
            	}
            	else {
            		transitaIgnorando(EstadoLexico.ES_COMENTARIO);
            	}
            	break;
			}
		}
	}

	private void transita(EstadoLexico aEstado) throws IOException {
		lexema = lexema + (char) caract;
		transitaIgnorando(aEstado);
	}

	private void transitaIgnorando(EstadoLexico aEstado) throws IOException {
		estado = aEstado;
		filaInicio = fila;
		colInicio = col;
		caract = sigCar();
	}

	private int sigCar() throws IOException {
		caract = input.read();
		if (caract == '\n') {
			col = -1;
			fila++;
		} else {
			col++;
		}
		return caract;
	}

	private boolean esLetra(int car) {
		return (car >= 'a' && car <= 'z') || (car >= 'A' && car <= 'Z');
	}

	private boolean esDigito(int car) {
		return (car >= '0' && car <= '9');
	}

	private boolean esSep(int car) {
		return car == ' ' || car == '\t' || car == '\n' || car == '\r'
				|| car == '\b';
	}

	private void errorLexico() {
		System.err.println("(" + fila + "," + col + ")"
				+ "ERROR LEXICO: caracter desconocido:" + (char) caract);
		System.exit(1);
	}

	;

	private Token tokenSimbolo() {
		if (lexema.equals("true")) {
			return new Token(filaInicio, colInicio, CatLexica.TRUE);
		} else if (lexema.equals("false")) {
			return new Token(filaInicio, colInicio, CatLexica.FALSE);
		} else if (lexema.equals("and")) {
			return new Token(filaInicio, colInicio, CatLexica.AND);
		}else if (lexema.equals("or")) {
			return new Token(filaInicio, colInicio, CatLexica.OR);
		}else if (lexema.equals("not")) {
			return new Token(filaInicio, colInicio, CatLexica.NOT);
		}else if (lexema.equals("eq")) {
			return new Token(filaInicio, colInicio, CatLexica.EQ);
		}else if (lexema.equals("neq")) {
			return new Token(filaInicio, colInicio, CatLexica.NEQ);
		}else if (lexema.equals("lt")) {
			return new Token(filaInicio, colInicio, CatLexica.LT);
		}else if (lexema.equals("le")) {
			return new Token(filaInicio, colInicio, CatLexica.LE);
		}else if (lexema.equals("gt")) {
			return new Token(filaInicio, colInicio, CatLexica.GT);
		}else if (lexema.equals("ge")) {
			return new Token(filaInicio, colInicio, CatLexica.GE);
		}else if (lexema.equals("if")) {
			return new Token(filaInicio, colInicio, CatLexica.IF);
		}else if (lexema.equals("fi")) {
			return new Token(filaInicio, colInicio, CatLexica.FI);
		}else if (lexema.equals("do")) {
			return new Token(filaInicio, colInicio, CatLexica.DO);
		}else if (lexema.equals("od")) {
			return new Token(filaInicio, colInicio, CatLexica.OD);
		}else if (lexema.equals("case")) {
			return new Token(filaInicio, colInicio, CatLexica.CASE);
		}else if (lexema.equals("int")) {
			return new Token(filaInicio, colInicio, CatLexica.INT);
		}else if (lexema.equals("boolean")) {
			return new Token(filaInicio, colInicio, CatLexica.BOOLEAN);
		}else {
			return new Token(filaInicio, colInicio, CatLexica.IDENTIFICADOR, lexema);
		}
	}
}
